// 常用公共方法
export function _getQueryString(name) {
	/* 提取url参数 */
	var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
	var r = window.location.search.substr(1).match(reg)
	if (r != null) return unescape(r[2])
	return null
}

export function splitNum(s) {
	// 截断 123.4567 = 123.45  不进位
	if (!s) return 0
	let arr = s.toString().split('.'),
		integer = arr[0],
		fractions = arr[1]
	return `${integer}.${fractions ? fractions.substr(0, 2) : '00'}`
}

export function checkEmpty(arr) {
	/**
   * @Description: 验证表单输入是否为空
   * @param {type} Array || Object
   * @return: Boolean
   * @Author: Eve
   * @Date: 2018-12-27 11:20:54
   * @LastEditors: Eve
   * @LastEditTime: Do not edit
   * arr=['...'||{val:'...'}]
   */
	let lock = true

	for (let i = 0, val; (val = arr[i++]); ) {
		if (typeof val === 'string') {
			if (val === '') {
				lock = false
				this.$errorTip('请勿输入空值')
				break
			}
		} else {
			if (val.value === '') {
				lock = false
				this.$errorTip(val.tip)
				break
			}
		}
	}
	return lock
}

export function httpResDeal(res) {
	/**
   * @Description: 请求统一状态处理
   * @param {type} Request
   * @return: Object || Boolean
   * @Author: Eve
   * @Date: 2018-12-27 11:22:03
   * @LastEditors: Eve
   * @LastEditTime: Do not edit
   */
	let { code, msg, message } = res.data
	if (code === 1) {
		return res.data
	}
	this.$msg.error(msg || message || '请求失败')
	return false
}

export function setStorage({ name, data, strify = false, vtime = null }) {
	/**
   * @Description: 设置缓存
   * @param {Object} name->名字 data->数据 strify->是否需要序列化 vtime->过期时间(天)
   * @return: null
   * @Author: Eve
   * @Date: 2019-01-21 16:15:02
   * @LastEditors: Eve
   * @LastEditTime: Do not edit
   */
	data = strify ? JSON.stringify(data) : data
	if (vtime) {
		vtime *= 24 * 60 * 60 * 1000
		data += '__vtime__' + vtime + new Date().getTime()
	}
	localStorage.setItem(name, data)
}

export function getStorage(name, strify = false) {
	/**
   * @Description: 获取缓存
   * @param {type} name->缓存名  strify -> 是否需要JSON化
   * @return: bool || string || Object
   * @Author: Eve
   * @Date: 2019-01-21 16:13:31
   * @LastEditors: Eve
   * @LastEditTime: Do not edit
   */
	let tmp = localStorage.getItem(name)
	if (!tmp) return false
	let arr = tmp.split('__vtime__'),
		vtime = arr[1],
		data = arr[0]
	if (vtime && vtime < new Date().getTime()) {
		console.warn(`${name} expired!`)
		localStorage.removeItem(name)
		return false
	} else {
		return strify ? JSON.parse(data) : data
	}
}

/* 
传入不同的判断类型来返回不同的判断函数
例:  isArray = isType('Array')
*/
export const isType = type => target => `[object ${type}]` === Object.prototype.toString.call(target)

export function deepCopy(o) {
	/* 
  浅拷贝只是拷贝了内存栈中的数据，
  深拷贝，则是要沿着引用类型变量的真实内存地址，
  去进行一次次的深度遍历，直到拷贝完目标遍历在栈与堆中的所有真实值。

  JSON方式实现的深拷贝，有很多缺陷，首先，是拷贝失真：

  1. 值为undefined、函数、Symbol的属性，或者键为Symbol字符串的属性，拷贝后，属性会丢失。
  2. 值为NaN的属性，拷贝后，值转为了null。
  3. 值为非标准对象Object，比如Set、Map、Error、RegExp等等的属性或数组元素
  ，拷贝后，值转为了空的标准对象，丢失了原来的原型继承关系。
  4. 值为undefined、NaN、函数的数组元素，拷贝后，值转为了null。

  拷贝功能的缺陷：

  1. 原型链丢失
  2. 无法拷贝有循环引用的对象


  深拷贝 = 浅拷贝+深度遍历+特殊情况容错。

  */

	// 先判断是否是字面量 true直接浅拷贝即可
	if (isPrimitive(o)) return o
	// 深度遍历一般有两种选择，一个是递归，一个是while循环。
	/* 
  递归很好理解，但有个缺陷，大量函数栈帧的入栈，
  很容易导致内存空间不足而爆栈，特别是对于有循环引用关系的输入，
  可能秒秒钟爆炸。这里，我们采用while循环。
  采用while循环的话，我们可以模拟一个栈结构，
  栈如果为空，则结束循环，若不为空，则进行循环，
  循环第一步，先出栈，然后处理数据，处理完之后，进入下一次循环判断。
  在JS里，模拟栈结构可以用数组，push与pop组合，完美实现后入先出。
  在数据结构与算法里，这叫深度优先
  */

	// 首先，先定义一个观察者，用来记录遍历的结果。等到遍历结束，这个观察者就是深拷贝的结果。
	const observer = {}

	// 然后，用数组模拟一个栈结构
	const nodeList = [
		{
			key: null // 这里，增加一个key属性，用来关联每次遍历所要处理的数据。
		}
	]

	// 其次，为了每次遍历时能地做一些处理，入栈的数据用对象来表示比较合适。

	// 循环遍历
	let node = null
	while (nodeList.length > 0) {
		node = nodeList.pop() // 出栈，深度优先

		// 处理节点node

		/* 
    1.特殊情况处理，比如Symbol类型的属性虽然无法被Object.keys迭代出来，
    但可以用Reflect.ownKeys来解决。又比如，针对循环引用，可以在循环外面建立哈希表
    ，每次循环都判断要处理的输入是否已存在哈希表，如果存在，直接引用，否则，存入哈希表。
     */
		// 用WeakMap模拟哈希表 , 它的弱引用可以避免内存泄漏
		const hashMap = new WeakMap()
		// 遍历包括Symbol类型在内的所有属性
		const keys = Reflect.ownKeys(node.value)

		// 初始化
		if (node.key === null) {
			node.value = o
			node.observer = observer
			// 存入哈希表
			hashmap.set(node.value, node.observer)
		}

		/* 
    3.对节点的属性进行遍历，属性值为引用类型，将它压入栈，
    否则，观察者利用关联的key记录属性值，然后进入下一次循环。 
    */
		for (let i = keys.length, key; (key = [--i]); ) {
			// 是字面量，直接记录
			if (isPrimitive(value)) {
				node.observer[key] = value
				continue
			}
			// 否则，入栈
			nodeList.push({
				key,
				value,
				observer: node.observer
			})
			// 查询哈希表，如果不存在对象key，就存入哈希表
			if (!hashmap.has(node.value)) {
				hashmap.set(node.value, (node.observer[node.key] = Array.isArray(node.value) ? [] : {}))
				// 将对象压入栈
				nodeList.push({
					key: node.key,
					value: node.value,
					observer: node.observer[node.key]
				})
				continue
			} else if (node.observer !== hashmap.get(node.value)) {
				// 存在哈希表里，则从哈希表里取出，赋值
				node.observer[node.key] = hashmap.get(node.value)
				continue
			}
		}
	}
}

export function isPrimitive(o) {
	// 判断输入是否为字面量
	if (typeof o !== 'function' && typeof o !== 'object') return true
	if (o === null) return true
	return false
}
